package com.lefevre.cms.utils;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;

/**
 * 进制转换
 *
 * @author Lefevre
 */
public class HexConversion {
    final static char[] digits = {
            '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f', 'g', 'h',
            'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F',
            'G', 'H', 'I', 'J', 'K', 'L',
            'M', 'N', 'O', 'P', 'Q', 'R',
            'S', 'T', 'U', 'V', 'W', 'X',
            'Y', 'Z', '+', '/',
    };
    private static final Logger logger = LoggerFactory.getLogger(HexConversion.class);

    /**
     * 字符串转换为十六进制
     *
     * @param strValue
     * @return
     */
    public static String getStringToHex(String strValue) {
        byte[] byteData = null;
        int intHex = 0;
        StringBuilder strHex = new StringBuilder();
        String strReturn = "";
        try {
            byteData = strValue.getBytes(StandardCharsets.UTF_8);
            for (byte byteDatum : byteData) {
                intHex = (int) byteDatum;
                if (intHex < 0)
                    intHex += 256;
                if (intHex < 16)
                    strHex.append("0").append(Integer.toHexString(intHex).toUpperCase());
                else
                    strHex.append(Integer.toHexString(intHex).toUpperCase());
            }
            strReturn = strHex.toString();
        } catch (Exception ex) {
            logger.error("字符串转换为十六进制", ex);
        }
        return strReturn;
    }

    /**
     * 十六进制转换为字符串
     *
     * @param strValue
     * @return
     */
    public static String getHexToString(String strValue) {
        int intCounts = strValue.length() / 2;
        String strReturn = "";
        String strHex = "";
        int intHex = 0;
        byte byteData[] = new byte[intCounts];
        try {
            for (int intI = 0; intI < intCounts; intI++) {
                strHex = strValue.substring(0, 2);
                strValue = strValue.substring(2);
                intHex = Integer.parseInt(strHex, 16);
                if (intHex > 128)
                    intHex = intHex - 256;
                byteData[intI] = (byte) intHex;
            }
            strReturn = new String(byteData, "UTF-8");
        } catch (Exception ex) {
            logger.error("十六进制转换为字符串", ex);
        }
        return strReturn;
    }

    /**
     * byte[]转换为十六进制
     *
     * @param b
     * @return
     */
    public static String byteToHex(byte[] b) {
        if (b == null || b.length <= 0) {
            return null;
        }
        return Hex.encodeHexString(b);
    }

    /**
     * 把16进制字符串转换成字节数组
     *
     * @param hexString
     * @return byte[]
     */
    public static byte[] hexStringToByte(String hex) {
        if (hex == null || "".equals(hex)) {
            return null;
        }
        char[] cs = hex.toCharArray();
        try {
            return Hex.decodeHex(cs);
        } catch (DecoderException e) {
            logger.error("把16进制字符串转换成字节数组错误", e);
        }
        return null;
    }


    /**
     * 把10进制的数字转换成64进制
     *
     * @param number
     * @param shift  进制
     * @return
     */
    public static String CompressNumber(long number, int shift) {
        char[] buf = new char[64];
        int charPos = 64;
        int radix = 1 << shift;
        long mask = radix - 1;
        do {
            buf[--charPos] = digits[(int) (number & mask)];
            number >>>= shift;
        } while (number != 0);
        return new String(buf, charPos, (64 - charPos));
    }

    /**
     * 把64进制的字符串转换成10进制
     *
     * @param decompStr
     * @return
     */
    public static long UnCompressNumber(String decompStr) {
        long result = 0;
        for (int i = decompStr.length() - 1; i >= 0; i--) {
            if (i == decompStr.length() - 1) {
                result += getCharIndexNum(decompStr.charAt(i));
                continue;
            }
            for (int j = 0; j < digits.length; j++) {
                if (decompStr.charAt(i) == digits[j]) {
                    result += ((long) j) << 6 * (decompStr.length() - 1 - i);
                }
            }
        }
        return result;
    }

    /**
     * @param ch
     * @return
     */
    private static long getCharIndexNum(char ch) {
        int num = ((int) ch);
        if (num >= 48 && num <= 57) {
            return num - 48;
        } else if (num >= 97 && num <= 122) {
            return num - 87;
        } else if (num >= 65 && num <= 90) {
            return num - 29;
        } else if (num == 43) {
            return 62;
        } else if (num == 47) {
            return 63;
        }
        return 0;
    }
}
